home *** CD-ROM | disk | FTP | other *** search
/ Risc World 5 / Risc World 5.iso / SOFTWARE / Issue5 / PD / DIRSYNC / LegalStuff / bascat / bascat.c next >
C/C++ Source or Header  |  2002-11-24  |  20KB  |  781 lines

  1. /* -*-c-*-
  2.  *
  3.  * $Id: bascat.c,v 1.1 1997/07/23 01:19:33 mdw Exp mdw $
  4.  *
  5.  * Display BBC BASIC programs more or less anywhere
  6.  *
  7.  * (c) 1996, 1997 Matthew Wilcox and Mark Wooding
  8.  * (c) 1999 Darren Salt
  9.  */
  10.  
  11. /*----- Licensing notice --------------------------------------------------*
  12.  *
  13.  * This file is part of Bascat.
  14.  *
  15.  * Bascat is free software; you can redistribute it and/or modify it
  16.  * under the terms of the GNU Library General Public License as
  17.  * published by the Free Software Foundation; either version 2 of the
  18.  * License, or (at your option) any later version.
  19.  *
  20.  * Bascat is distributed in the hope that it will be useful, but
  21.  * WITHOUT ANY WARRANTY; without even the implied warranty of
  22.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  23.  * GNU Library General Public License for more details.
  24.  *
  25.  * You should have received a copy of the GNU General Public License
  26.  * along with Bascat; if not, write to the Free Software Foundation,
  27.  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  28.  */
  29.  
  30. /*----- Revision history --------------------------------------------------*
  31.  *
  32.  * Revision 1.2.1  1999/06/19  ds
  33.  * Compilable under RISC OS (using shared C library & libgnu); no -p, -l.
  34.  * Various other things :-)
  35.  *
  36.  * $Log: bascat.c,v $
  37.  * Revision 1.1  1997/07/23 01:19:33  mdw
  38.  * Initial revision
  39.  *
  40.  */
  41.  
  42. /*----- Header files ------------------------------------------------------*/
  43.  
  44. /* --- ANSI library headers --- */
  45.  
  46. #include <ctype.h>
  47. #include <errno.h>
  48. #include <signal.h>
  49. #include <stdio.h>
  50. #include <stdlib.h>
  51. #include <string.h>
  52.  
  53. /* --- GNU library headers --- */
  54.  
  55. #include <error.h>
  56.  
  57. /* --- Operating system specific headers --- */
  58.  
  59. #ifdef HAVE_LIBTERMCAP
  60. #  include <termcap.h>
  61. #endif
  62. #ifdef USING_ACORN_SHAREDCLIB
  63. /* also libgnu <URL:ftp://ftp.zap.uk.eu.org/pub/ds/GNU/src/libgnu.zip> */
  64. #  include "system.h"
  65. #  include "ro_wild.h"
  66. #else
  67. #  include <unistd.h>
  68. #endif
  69.  
  70. /* --- Private headers --- */
  71.  
  72. #include "mdwopt.h"
  73.  
  74. /*----- Version information -----------------------------------------------*/
  75.  
  76. #ifndef NDEBUG
  77. #  define D(x) x
  78. #else
  79. #  define D(x)
  80. #endif
  81.  
  82. /*----- Tokenisation tables -----------------------------------------------*
  83.  *
  84.  * These tables are from the BBC BASIC guide.  Additional verification
  85.  * carried out on an A440 with RISC OS 3.1
  86.  */
  87.  
  88. static const char *bcTok__base[] = {
  89.   "OTHERWISE",
  90.   "AND", "DIV", "EOR", "MOD", "OR", "ERROR", "LINE", "OFF",
  91.   "STEP", "SPC", "TAB(", "ELSE", "THEN", "*", "OPENIN", "PTR",
  92.   "PAGE", "TIME", "LOMEM", "HIMEM", "ABS", "ACS", "ADVAL", "ASC",
  93.   "ASN", "ATN", "BGET", "COS", "COUNT", "DEG", "ERL", "ERR",
  94.   "EVAL", "EXP", "EXT", "FALSE", "FN", "GET", "INKEY", "INSTR(",
  95.   "INT", "LEN", "LN", "LOG", "NOT", "OPENUP", "OPENOUT", "PI",
  96.   "POINT(", "POS", "RAD", "RND", "SGN", "SIN", "SQR", "TAN",
  97.   "TO", "TRUE", "USR", "VAL", "VPOS", "CHR$", "GET$", "INKEY$",
  98.   "LEFT$(", "MID$(", "RIGHT$(", "STR$", "STRING$(", "EOF", "*", "*",
  99.   "*", "WHEN", "OF", "ENDCASE", "ELSE", "ENDIF", "ENDWHILE", "PTR",
  100.   "PAGE", "TIME", "LOMEM", "HIMEM", "SOUND", "BPUT", "CALL", "CHAIN",
  101.   "CLEAR", "CLOSE", "CLG", "CLS", "DATA", "DEF", "DIM", "DRAW",
  102.   "END", "ENDPROC", "ENVELOPE", "FOR", "GOSUB", "GOTO", "GCOL", "IF",
  103.   "INPUT", "LET", "LOCAL", "MODE", "MOVE", "NEXT", "ON", "VDU",
  104.   "PLOT", "PRINT", "PROC", "READ", "REM", "REPEAT", "REPORT", "RESTORE",
  105.   "RETURN", "RUN", "STOP", "COLOUR", "TRACE", "UNTIL", "WIDTH", "OSCLI"
  106. };
  107.  
  108. static const char *bcTok__c6[] = {
  109.   "SUM", "BEAT"
  110. };
  111.  
  112. static const char *bcTok__c7[] = {
  113.   "APPEND", "AUTO",
  114.   "CRUNCH", "DELETE", "EDIT", "HELP", "LIST", "LOAD", "LVAR", "NEW",
  115.   "OLD", "RENUMBER", "SAVE", "TEXTLOAD", "TEXTSAVE", "TWIN", "TWINO",
  116.     "INSTALL"
  117. };
  118.  
  119. static const char *bcTok__c8[] = {
  120.   "CASE", "CIRCLE",
  121.   "FILL", "ORIGIN", "POINT", "RECTANGLE", "SWAP", "WHILE", "WAIT", "MOUSE",
  122.   "QUIT", "SYS", "INSTALL", "LIBRARY", "TINT", "ELLIPSE", "BEATS", "TEMPO",
  123.   "VOICES", "VOICE", "STEREO", "OVERLAY"
  124. };
  125.  
  126. #define ITEMS(array) (sizeof(array) / sizeof((array)[0]))
  127.  
  128. /*----- Static variables --------------------------------------------------*/
  129.  
  130. enum {
  131.   s_keyword,                /* Expecting a keyword next */
  132.   s_normal,                /* Normal state, reading input */
  133.   s_comment,                /* In a command (or literal *cmd) */
  134.   s_quote,                /* Inside a quoted string */
  135.   s_8d,                    /* Inside an embedded line number */
  136.   s_c6, s_c7, s_c8,            /* Various shift states */
  137.   s_dummy
  138. };
  139.  
  140. static char bc__state = s_normal;    /* Current detokenisation state */
  141.  
  142. static long bc__lineno;        /* The bytes representing the line number */
  143. static long bc__lineno_count;    /* The number of bytes unread */
  144.  
  145. enum {
  146.   f_goto_harmful = 1,            /* GOTO etc. considered harmful */
  147.   f_leave_tokenised = 2,        /* Don't detokenise (except line nos) */
  148.   f_linenumbers = 4,            /* Display linenumbers on left */
  149.   f_many_files = 1<<8,            /* More than one file given */
  150.   f_lineno_warn = 1<<9,            /* We've encountered a line number */
  151. #ifdef __riscos
  152.   f_allow_ctrl = 1<<16,            /* Don't complain about ctrl chars */
  153. #else
  154.   f_highlight = 1<<16,            /* Highlight keywords and things */
  155.   f_tty = 1<<17,            /* We're writing to TTY (or pager) */
  156.   f_less = 1<<18,            /* We're piping through `less' */
  157. #endif
  158.   f_dummy
  159. };
  160.  
  161. static int bc__flags;            /* Various options flags */
  162.  
  163. #ifdef HAVE_LIBTERMCAP
  164. static char bc__termcap[2048];        /* Terminal capabilities buffer */
  165. #endif
  166.  
  167. #ifndef __riscos
  168. static char *bc__pager = 0;        /* Pointer to pager to use */
  169. #endif
  170.  
  171. const char *program_name = 0;        /* Program name (for GNU error()) */
  172.  
  173. /*----- Main code ---------------------------------------------------------*/
  174.  
  175. /* --- @bc__badprogram@ --- *
  176.  *
  177.  * Arguments:    @const char *msg@ = the error message
  178.  *        @const char *name@ = the file providing the input stream
  179.  *
  180.  * Returns:    1
  181.  *
  182.  * Use:        Displays a "bad program" error via GNU error().
  183.  */
  184.  
  185. static int bc__badprogram(const char *msg, const char *name)
  186. {
  187.   if (name)
  188.     error(0, 0, "%s: bad program: %s", name, msg);
  189.   else
  190.     error(0, 0, "bad program: %s", msg);
  191.   return 1;
  192. }
  193.  
  194. /* --- @bc__programerror@ --- *
  195.  *
  196.  * Arguments:    @const char *msg@ = the error message
  197.  *        @const char *name@ = the file providing the input stream
  198.  *
  199.  * Returns:    1
  200.  *
  201.  * Use:        Displays a general error message via GNU error().
  202.  */
  203.  
  204. static int bc__programerror(const char *msg, const char *name)
  205. {
  206.   if (name)
  207.     error(0, 0, "%s: %s", name, msg);
  208.   else
  209.     error(0, 0, "%s", msg);
  210.   return 1;
  211. }
  212.  
  213. /* --- @bc__keyword@ --- *
  214.  *
  215.  * Arguments:    @char *s@ = pointer to keyword string
  216.  *        @FILE *fp@ = stream to write onto
  217.  *
  218.  * Returns:    --
  219.  *
  220.  * Use:        Displays a keyword in a nice way.  There's some nasty hacking
  221.  *        here to make GNU's `less' work properly.  `more' appears to
  222.  *        cope with highlighting codes OK, so that's fine.  `less'
  223.  *        prefers it if we attempt to `overstrike' the bolded
  224.  *        characters.  What fun...
  225.  */
  226.  
  227. static void bc__keyword(const char *s, FILE *fp)
  228. {
  229. #ifdef HAVE_LIBTERMCAP
  230.   if ((~bc__flags & (f_less | f_highlight)) == 0) {
  231.     while (*s) {
  232.       putc(*s, fp);
  233.       putc(8, fp);            /* evil... */
  234.       putc(*s, fp);
  235.       s++;
  236.     }
  237.   } else {
  238.     static char buff[24];
  239.     static char *hs, *he, *p = buff;
  240.  
  241.     if (!hs) {
  242.       if (bc__flags & f_highlight) {
  243.     hs = tgetstr("md", &p);
  244.     he = tgetstr("me", &p);
  245.       } else
  246.     hs = he = "";
  247.     }
  248.     fputs(hs, fp);
  249.     fputs(s, fp);
  250.     fputs(he, fp);
  251.   }
  252. #else
  253.   fputs(s, fp);
  254. #endif
  255. }
  256.  
  257. /* --- @bc__mbtok@ --- *
  258.  *
  259.  * Arguments:    @int byte@ = the current byte
  260.  *        @const char *t[]@ = pointer to token table
  261.  *        @int n@ = number of items in token table
  262.  *        @FILE *fp@ = stream to write onto
  263.  *        @const char *name@ = the file providing the input stream
  264.  *
  265.  * Returns:    0 if everything's OK
  266.  *
  267.  * Use:        Decodes multibyte tokens.
  268.  */
  269.  
  270. static int bc__mbtok(int byte, const char *t[], int n, FILE * fp,
  271.              const char *name)
  272. {
  273.   byte -= 0x8E;
  274.   if (byte >= n)
  275.     return bc__badprogram("invalid multibyte token", name);
  276.   bc__keyword(t[byte], fp);
  277.   bc__state = s_normal;
  278.   return (0);
  279. }
  280.  
  281. /* --- @bc__decode@ --- *
  282.  *
  283.  * Arguments:    @int byte@ = byte to decode
  284.  *        @FILE *fp@ = stream to write onto
  285.  *        @const char *name@ = the file providing the input stream
  286.  *
  287.  * Returns:    0 if everything's going OK
  288.  *
  289.  * Use:        Decodes a byte, changing states as necessary.
  290.  */
  291.  
  292. static int bc__decode(int byte, FILE * fp, const char *name)
  293. {
  294.   switch (bc__state) {
  295.     case s_keyword:
  296.       if (byte == '*')
  297.     bc__state = s_comment;
  298.       else
  299.     bc__state = s_normal;
  300.       /* Fall through here */
  301.     case s_normal:
  302. #ifdef __riscos
  303.       if (bc__flags && f_allow_ctrl) {
  304.     if (byte == '\n')
  305.       return bc__programerror("sorry, cannot handle embedded line feeds", name);
  306.       } else
  307. #endif
  308.       if (byte < 0x20)
  309.     return bc__programerror("sorry, cannot handle embedded control characters", name);
  310.       if (byte >= 0x7F) {
  311.     switch (byte) {
  312.       case 0x8D:
  313.         if (!(bc__flags & (f_linenumbers | f_lineno_warn))) {
  314.           error (0, 0, "%sfound a line number token",
  315.                bc__flags & f_goto_harmful ? "" : "warning: ");
  316.           if (bc__flags & f_goto_harmful)
  317.         return 1;
  318.           bc__flags |= f_lineno_warn;
  319.         }
  320.         bc__state = s_8d;
  321.         bc__lineno = 0;
  322.         bc__lineno_count = 0;
  323.         break;
  324.       case 0xC6:
  325.         bc__state = s_c6;
  326.         break;
  327.       case 0xC7:
  328.         bc__state = s_c7;
  329.         break;
  330.       case 0xC8:
  331.         bc__state = s_c8;
  332.         break;
  333.       case 0x8B:            /* ELSE */
  334.       case 0x8C:            /* THEN */
  335.       case 0xF5:            /* REPEAT (a funny one) */
  336.         bc__state = s_keyword;
  337.         bc__keyword(bcTok__base[byte - 0x7F], fp);
  338.         break;
  339.       case 0xE4:            /* GOSUB */
  340.         if (bc__flags & f_goto_harmful)
  341.           return bc__programerror("found a GOSUB", name);
  342.         goto default_action;
  343.       case 0xE5:            /* GOTO */
  344.         if (bc__flags & f_goto_harmful)
  345.           return bc__programerror("found a GOTO", name);
  346.         goto default_action;
  347.       case 0xDC:            /* DATA */
  348.       case 0xF4:            /* REM */
  349.         bc__state = s_comment;
  350.         /* Fall through here */
  351.       default:
  352.       default_action:
  353.         if (bc__flags & f_leave_tokenised)
  354.           fputc(byte, fp);
  355.         else
  356.           bc__keyword(bcTok__base[byte - 0x7F], fp);
  357.         break;
  358.     }
  359.       } else {
  360.     if (byte == '"')
  361.       bc__state = s_quote;
  362.     fputc(byte, fp);
  363.       }
  364.       break;
  365.     case s_quote:
  366.       if (byte == '"')
  367.     bc__state = s_normal;
  368.       /* Fall through here */
  369.     case s_comment:
  370.       fputc(byte, fp);
  371.       break;
  372.     case s_8d:
  373.       bc__lineno |= byte << bc__lineno_count;
  374.       bc__lineno_count += 8;
  375.       if (bc__lineno_count == 24) {
  376.     int i = (bc__lineno >> 8) & 0x3F3F;
  377.     int j = bc__lineno ^ 0x54;
  378.     i |= (j & 0x30) << 2;
  379.     i |= (j & 0xC) << 12;
  380.     i |= (j & 3) << 16; /* hmm... */
  381.     fprintf (fp, "%i", i);
  382.     bc__state = s_normal;
  383.       }
  384.       break;
  385.     case s_c6:
  386.       if (bc__flags & f_leave_tokenised) {
  387.     fputc(0xC6, fp);
  388.     fputc(byte, fp);
  389.     return 0;
  390.       } else
  391.         return (bc__mbtok(byte, bcTok__c6, ITEMS(bcTok__c6), fp, name));
  392.       break;
  393.     case s_c7:
  394.       if (bc__flags & f_leave_tokenised) {
  395.     fputc(0xC7, fp);
  396.     fputc(byte, fp);
  397.     return 0;
  398.       } else
  399.     return (bc__mbtok(byte, bcTok__c7, ITEMS(bcTok__c7), fp, name));
  400.       break;
  401.     case s_c8:
  402.       if (bc__flags & f_leave_tokenised) {
  403.     fputc(0xC8, fp);
  404.     fputc(byte, fp);
  405.     return 0;
  406.       } else
  407.       return (bc__mbtok(byte, bcTok__c8, ITEMS(bcTok__c8), fp, name));
  408.       break;
  409.   }
  410.   return (0);
  411. }
  412.  
  413. /* --- @bc__line@ --- *
  414.  *
  415.  * Arguments:    @FILE *in@ = input stream to read
  416.  *        @FILE *out@ = output stream to write
  417.  *        @const char *name@ = the file providing the input stream
  418.  *
  419.  * Returns:    0 if there's another line after this one.
  420.  *        -1 if this was the last line.
  421.  *        1 if there was a format error (bad program etc.).
  422.  *
  423.  * Use:        Decodes a BASIC line into stuff to be written.
  424.  */
  425.  
  426. static int bc__line(FILE *in, FILE *out, const char *name)
  427. {
  428.   /* --- Read the line number --- */
  429.  
  430.   {
  431.     int a, b;
  432.  
  433.     a = getc(in);
  434.     D( fprintf(stderr, "ln_0 == %i\n", a); )
  435.     if (a == EOF)
  436.       goto eof;
  437.     if (a == 0xFF)
  438.       return (-1);
  439.  
  440.     b = getc(in);
  441.     D( fprintf(stderr, "ln_1 == %i\n", b); )
  442.     if (b == EOF)
  443.       goto eof;
  444.  
  445.     if (bc__flags & f_linenumbers)
  446.       fprintf(out, "%5i", (a << 8) + b);
  447.   }
  448.  
  449.   {
  450.     int len;
  451.     int byte;
  452.  
  453.     len = getc(in);
  454.     D( fprintf(stderr, "linelen == %i\n", len); )
  455.     if (len == EOF)
  456.       goto eof;
  457.     len -= 4;
  458.  
  459.     bc__state = s_normal;
  460.     while (len) {
  461.       byte = getc(in);
  462.       D( fprintf(stderr, "state == %i, byte == %i\n", \
  463.         bc__state, byte); )
  464.       if (byte == EOF)
  465.     goto eof;
  466.       if (bc__decode(byte, out, name))
  467.     return 1;
  468.       len--;
  469.     }
  470.     putc('\n', out);
  471.  
  472.     byte = getc(in);
  473.     D( fprintf(stderr, "eol == %i\n", byte); )
  474.     if (byte == EOF)
  475.       goto eof;
  476.     else if (byte != 0x0D)
  477.       return bc__badprogram("expected end-of-line delimiter", name);
  478.   }
  479.  
  480.   return (0);
  481.  
  482. eof:
  483.   return bc__badprogram("unexpected end-of-file", name);
  484. }
  485.  
  486. /* --- @bc__file@ --- *
  487.  *
  488.  * Arguments:    @FILE *in@ = the input stream
  489.  *        @FILE *out@ = the output stream
  490.  *        @const char *name@ = the file providing the input stream
  491.  *
  492.  * Returns:    0 if OK
  493.  *
  494.  * Use:        Decodes an entire file.
  495.  */
  496.  
  497. static int bc__file(FILE *in, FILE *out, const char *name)
  498. {
  499.   int byte;
  500.  
  501.   if (bc__flags & f_many_files)
  502.     fprintf(out, "%sREM >%s\n", (bc__flags & f_linenumbers) ? "    0" : "",
  503.         name);
  504.  
  505.   /* --- Check for the inital newline char --- */
  506.  
  507.   byte = getc(in);
  508.   if (byte != 0x0D)
  509.     return bc__badprogram("doesn't start with a newline", name);
  510.  
  511.   /* --- Now read the lines in one by one --- */
  512.  
  513.   while ((byte = bc__line(in, out, name)) == 0) ;
  514.   if (byte == 1)
  515.     return 1; /* if an error was reported by bc__line */
  516.  
  517.   /* --- Check that we're really at end-of-file --- */
  518.  
  519.   byte = getc(in);
  520.   if (byte != EOF)
  521.     bc__programerror("warning: found data after end of program", name);
  522.   return 0;
  523. }
  524.  
  525. /* --- @bc__sigPipe@ --- *
  526.  *
  527.  * Arguments:    @int s@ = signal number
  528.  *
  529.  * Returns:    Doesn't
  530.  *
  531.  * Use:        Handles SIGPIPE signals, and gracefully kills the program.
  532.  */
  533.  
  534. #if defined SIGPIPE && !defined __riscos
  535. static void bc__sigPipe(int s)
  536. {
  537.   (void) s;
  538.   exit(0);                /* Gracefully, oh yes */
  539. }
  540. #endif
  541.  
  542. /* --- @bc__options@ --- *
  543.  *
  544.  * Arguments:    @int c@ = number of arguments
  545.  *        @char *v[]@ = pointer to arguments
  546.  *        @char *s@ = pointer to short options
  547.  *        @struct option *o@ = pointer to long options
  548.  *
  549.  * Returns:    --
  550.  *
  551.  * Use:        Parses lots of arguments.
  552.  */
  553.  
  554. static void bc__options(int c, char *v[], const char *s,
  555.             const struct option *o)
  556. {
  557.   int i;
  558.  
  559.   for (;;) {
  560.     i = mdwopt(c, v, s, o, 0, 0, gFlag_negation | gFlag_envVar);
  561.     if (i == -1)
  562.       break;
  563.  
  564.     switch (i) {
  565.       case 'v':
  566.       case 'h':
  567.     printf("%s v. " VERSION " (" __DATE__ ")\n", program_name);
  568.     if (i == 'v')
  569.       exit(0);
  570.     printf("\n"
  571. #ifdef __riscos
  572. "Usage: %s [OPTION]... [FILE]...\n"
  573. #else
  574. "Usage: %s [OPTION]... [FILE]...\n"
  575. #endif
  576. "\n"
  577. "Types BBC BASIC programs in a readable way.  Options currently supported are as\n"
  578. "follows:\n"
  579. "\n"
  580. "  -n, --line-numbers       displays line numbers for each line\n"
  581. "  -g, --goto-harmful       error if GOTO, GOSUB or a line number is used\n"
  582. "  -t, --leave-as-tokens    don't detokenise (except for line numbers)\n"
  583. #ifdef __riscos
  584. "  -c, --allow-ctrl-chars   don't complain about most embedded control codes\n"
  585. #else
  586. "  -l, --highlight          attempts to highlight keywords\n"
  587. "  -p, --pager=PAGER        sets pager to use (default $PAGER)\n"
  588. #endif
  589. "  -h, --help               displays this help text\n"
  590. "  -v, --version            displays the current version number\n"
  591. "\n"
  592. "Prefix long options with `no-' to cancel them.  Use `+' to cancel short options.\n"
  593. "Options can also be placed in the `BASCAT' environment variable, if you don't\n"
  594. "like the standard settings.\n",
  595.            program_name);
  596.     exit(0);
  597.     break;
  598.       case 'n':
  599.     bc__flags |= f_linenumbers;
  600.     break;
  601.       case 'n' | gFlag_negated:
  602.     bc__flags &= ~f_linenumbers;
  603.     break;
  604.       case 'g':
  605.     bc__flags |= f_goto_harmful;
  606.     break;
  607.       case 'g' | gFlag_negated:
  608.     bc__flags &= ~f_goto_harmful;
  609.     break;
  610.       case 't':
  611.     bc__flags |= f_leave_tokenised;
  612.     break;
  613.       case 't' | gFlag_negated:
  614.     bc__flags &= ~f_leave_tokenised;
  615.     break;
  616. #ifdef __riscos
  617.       case 'c':
  618.     bc__flags |= f_allow_ctrl;
  619.     break;
  620.       case 'c' | gFlag_negated:
  621.     bc__flags &= ~f_allow_ctrl;
  622.     break;
  623. #else
  624.       case 'l':
  625.     bc__flags |= f_highlight;
  626.     break;
  627.       case 'l' | gFlag_negated:
  628.     bc__flags &= ~f_highlight;
  629.     break;
  630.       case 'p':
  631.     bc__pager = optarg;
  632.     break;
  633. #endif
  634.     }
  635.   }
  636. }
  637.  
  638. /* --- @main@ --- *
  639.  *
  640.  * Arguments:    @int argc@ = number of arguments
  641.  *        @char *argc[]@ = pointer to what the arguments are
  642.  *
  643.  * Returns:    0 if it all worked
  644.  *
  645.  * Use:        Displays BASIC programs.
  646.  */
  647.  
  648. int main(int argc, char *argv[])
  649. {
  650.   static struct option opts[] = {
  651.     { "help", 0, 0, 'h' },
  652.     { "version", 0, 0, 'v' },
  653.     { "line-numbers", gFlag_negate, 0, 'n' },
  654.     { "goto-harmful", gFlag_negate, 0, 'g' },
  655.     { "leave-as-tokens", gFlag_negate, 0, 't' },
  656. #ifdef __riscos
  657.     { "allow-ctrl-chars", gFlag_negate, 0, 'c' },
  658. #else
  659.     { "highlight", gFlag_negate, 0, 'l' },
  660.     { "pager", gFlag_argReq, 0, 'p' },
  661. #endif
  662.     { 0, 0, 0, 0 }
  663.   };
  664. #ifdef __riscos
  665.   static char *shortopts = "hvn+g+t+c+";
  666. #else
  667.   static char *shortopts = "hvn+g+t+l+p:";
  668. #endif
  669.   int ret = 0;
  670.  
  671.   program_name = argv[0];
  672.  
  673.   /* --- Parse the command line options --- */
  674.  
  675.   bc__options(argc, argv, shortopts, opts);
  676.  
  677.   /* --- Now do the job --- */
  678.  
  679.   if (optind == argc && isatty(0)) {
  680.     fprintf(stderr,
  681.         "%s: no filenames given, and standard input is a tty\n"
  682.         "To force reading from stdin, use `%s -'.  For help, type "
  683.         "`%s --help'.\n",
  684.         program_name, program_name, program_name);
  685.     exit(0);
  686.   }
  687.  
  688. #ifdef HAVE_LIBTERMCAP
  689.   if (bc__flags & f_highlight)
  690.     tgetent(bc__termcap, getenv("TERM"));
  691. #endif
  692.  
  693.   {
  694.     FILE *in;
  695. #ifdef __riscos
  696.     FILE *out = stdout;
  697. #else
  698.     FILE *out = 0;
  699.  
  700.     /* --- If output is to a terminal, try paging --- *
  701.      *
  702.      * All programs which spew text should do this ;-)
  703.      */
  704.  
  705.     if (isatty(1)) {
  706.       if (!bc__pager)
  707.     bc__pager = getenv("PAGER");
  708.       if (!bc__pager)
  709.     bc__pager = PAGER;        /* Worth a try */
  710.       if (bc__pager) {        /* Allow sensible behaviour if no pager */
  711.     if (strstr(bc__pager, "less"))
  712.       bc__flags |= f_less;        /* HACK!!! */
  713.     out = popen(bc__pager, "w");
  714.       }
  715.       if (!out)
  716.     out = stdout;
  717.       else {
  718.     bc__flags |= f_tty;
  719. #  ifdef SIGPIPE
  720.     signal(SIGPIPE, bc__sigPipe);
  721. #  endif
  722.       }
  723.     } else
  724.       out = stdout;
  725. #endif
  726.  
  727.     /* --- Now go through all the files --- */
  728.  
  729.     if (argc - optind > 1)
  730.       bc__flags |= f_many_files;
  731.  
  732.     if (optind == argc)
  733.       bc__file(stdin, out, "(standard input)");
  734.     else
  735.       while (optind < argc) {
  736. #ifdef __riscos
  737.     if (ro_wild_test(argv[optind])) {
  738.       ro_wild *wild = ro_wild_init(argv[optind]);
  739.       char *name;
  740.       bc__flags |= f_many_files;
  741.       while ((name = ro_wild_next(wild)) != 0) {
  742.         in = fopen(name, "rb");
  743.         if (!in) {
  744.           error(0, errno, "%s: couldn't open", name);
  745.           ret = 1;
  746.         } else {
  747.           ret |= bc__file(in, out, name);
  748.           fclose(in);
  749.         }
  750.       }
  751.       if (!ro_wild_finish(wild)) {
  752.         error(0, 0, "%s: couldn't open", argv[optind]);
  753.         ret = 1;
  754.       }
  755.     } else
  756. #endif
  757.     if (strcmp(argv[optind], "-") == 0)
  758.       bc__file(stdin, out, "(standard input)");
  759.     else {
  760.       in = fopen(argv[optind], "rb");
  761.       if (!in) {
  762.         error(0, errno, "%s: couldn't open", argv[optind]);
  763.         ret = 1;
  764.       } else {
  765.         ret |= bc__file(in, out, argv[optind]);
  766.         fclose(in);
  767.       }
  768.     }
  769.     optind++;
  770.       }
  771. #ifndef __riscos
  772.     if (bc__flags & f_tty)
  773.       pclose(out);
  774. #endif
  775.   }
  776.  
  777.   return (0);
  778. }
  779.  
  780. /*----- That's all, folks -------------------------------------------------*/
  781.